#include "log.h"
#include "file.h"
#include "stm32f4xx.h"
#include "usbd_cdc_if.h"

#define TAG "MAIN"

// #if !(__ARMCC_VERSION >= 6010050) /* 不是AC6编译器，即使用AC5编译器时 */
// #define __ALIGNED_8 __align(8)    /* AC5使用这个 */
// #else                             /* 使用AC6编译器时 */
// #define __ALIGNED_8 __ALIGNED(8)  /* AC6使用这个 */
// #endif

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务 守护任务
 */
#define START_TASK_PRIO 15           /* 任务优先级 */
#define START_STK_SIZE 128           /* 任务堆栈大小 */
TaskHandle_t StartTask_Handler;      /* 任务句柄 */
void start_task(void *pvParameters); /* 任务函数 */

/* TASK1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 * 堆栈太小会导致使用打印函数卡死
 */
#define TASK1_PRIO 16           /* 任务优先级 */
#define TASK1_STK_SIZE 256      /* 任务堆栈大小 */
TaskHandle_t Task1Task_Handler; /* 任务句柄 */
void task1(void *pvParameters); /* 任务函数 */

/* TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK2_PRIO 15                    /* 任务优先级 */
#define TASK2_STK_SIZE 1024              /* 任务堆栈大小 */
TaskHandle_t Task2Task_Handler;          /* 任务句柄 */
extern void startUI(void *pvParameters); /* 任务函数 */

#define VERSION "STM32F407ZGT6-V1.0.0"
#define HARDINFO "FLASH:1024KB+2MB SRAM:192KB+1024KB"
#define COPYRIGHT "Copyright (C) 2025-2035@fengyu.com"

/**
 * @brief       显示错误信息,停止运行,持续提示错误信息
 * @param       x, y : 坐标
 * @param       err  : 错误信息
 * @param       fsize: 字体大小
 * @retval      无
 */
void system_error_show(uint16_t x, uint16_t y, char *err)
{
    ERR(err);
    while (1)
    {
        LCD_SHOW_STRING(x, y, LCD_Width, LCD_Height, LCD_FONT, err, RED);
        delay_ms(500);
        LED_TOGGLE;
        LCD_ClearRect(x, y, LCD_Width - 1, y + LCD_FONT);
        delay_ms(500);
        LED_TOGGLE;
    }
}

void lcd_log_show(uint16_t x, uint16_t y, char *msg, uint16_t color)
{
    LCD_SHOW_STRING(x, y, LCD_Width, LCD_Height, LCD_FONT, msg, color);
    INFO(msg);
}

/**
 * @brief       外部内存测试(最大支持1M字节内存测试)
 * @param       x, y : 坐标
 * @param       fsize: 字体大小
 * @retval      0,成功; 1,失败.
 */
uint8_t system_exsram_test(uint16_t x, uint16_t y)
{
    uint32_t i = 0;
    uint16_t temp = 0;

    LCD_SHOW_STRING(x, y, LCD_Width, y + LCD_FONT, LCD_FONT, "Ex Memory Test(KB):", WHITE);
    LCD_SHOW_STRING(x + LCD_OFFSET, y, LCD_Width, y + LCD_FONT, LCD_FONT, "   0", WHITE);

    /* 每隔1K字节,写入一个数据,总共写入1024个数据,刚好是1M字节 */
    for (i = 0; i < 1024 * 1024; i += 1024)
    {
        sram_write((uint8_t *)&i, i, 2);
    }

    /* 依次读出之前写入的数据,进行校验 */
    for (i = 0; i < 1024 * 1024; i += 1024)
    {
        sram_read((uint8_t *)&temp, i, 2);

        if (temp != (uint16_t)i)
        {
            ERR("mem error:%d %d", (uint16_t)i, temp);
            break;
        }

        LCD_SHOW_XNUM(x + LCD_OFFSET, y, (uint16_t)(i / 1024 + 1), 4, LCD_FONT, 0, WHITE); /* 显示内存容量 */
    }

    if (i >= 1024 * 1024)
    {
        LCD_SHOW_XNUM(x + LCD_OFFSET, y, i / 1024, 4, LCD_FONT, 0, WHITE); /* 显示内存值 */
        return 0;                                                          /* 内存正常,成功 */
    }

    return 1; /* 失败 */
}

/**
 * @brief       擦除整个SPI FLASH(即所有资源都删除),以快速更新系统.
 * @retval      0,没有擦除; 1,擦除了;
 */
static uint8_t system_files_erase(uint16_t x, uint16_t y)
{
    uint8_t key;
    uint8_t t = 0;

    lcd_log_show(x, y, "Erase all system files?", WHITE);
    lcd_log_show(x, y + LCD_FONT, "KEY0:NO / KEY1:YES", WHITE);
    while (1)
    {
        t++;

        if (t % 10 == 0)
        {

            LED_TOGGLE;
        }

        key = key_scan(0);

        if (key == KEY0_PRES) /* 不擦除,用户取消了 */
        {
            lcd_log_show(x + LCD_OFFSET, y + LCD_FONT, "NO", WHITE);
            return 0;
        }

        if (key == KEY1_PRES) /* 要擦除,要重新来过 */
        {
            lcd_log_show(x + LCD_OFFSET, y + LCD_FONT, "YES", RED);
            lcd_log_show(x, y + LCD_FONT * 2, "Flash erase,keep the power on!", RED);
            W25QXX_Erase_Chip(); /* 全片擦除 效率高 */
            lcd_log_show(x + LCD_OFFSET, y + LCD_FONT * 2, "OK", WHITE);
            delay_ms(500);
            return 1;
        }
        delay_ms(100);
    }
}

int main(void)
{
    uint16_t res, temp;
    uint32_t dtsize, dfsize;
    uint16_t ypos = 0, j = 0;
    char *info;
    HAL_Init();
    sys_stm32_clock_init(112, 8, 2, 7);
    delay_init(168);
    usart_init(115200);
    my_mem_init(SRAMIN);
    my_mem_init(SRAMCCM);
    printf("%s\r\n", VERSION);
    printf("%s\r\n", HARDINFO);
    printf("SystemCoreClock:%dM\r\n", SystemCoreClock / 1000000);
    printf("%s\r\n", COPYRIGHT);
    printf("Build Info:%s - %s\r\n", __DATE__, __TIME__);
    info = mymalloc(SRAMCCM, 32); /* 申请31个字节内存 */
    if (info == NULL)
    {
        FATAL("mallloc info err");
    }
    led_init();
    key_init();
    lcd_init();
    j = 0;
    ypos = 2;
    delay_ms(500);
    /* 显示版权信息 */
    LCD_SHOW_STRING(5, ypos, LCD_Width, LCD_Height, LCD_ICON, "Z", DARKBLUE);
    LCD_SHOW_STRING(5, ypos + LCD_ICON, LCD_Width, LCD_Height, LCD_ICON, "Y", DARKBLUE);
    LCD_SHOW_STRING(LCD_ICON + 5 * 2, ypos + LCD_FONT * j++, LCD_Width, LCD_Height, LCD_FONT, VERSION, WHITE);
    LCD_SHOW_STRING(LCD_ICON + 5 * 2, ypos + LCD_FONT * j++, LCD_Width, LCD_Height, LCD_FONT, HARDINFO, WHITE);
    sprintf(info, "SystemCoreClock:%dM", SystemCoreClock / 1000000);
    LCD_SHOW_STRING(LCD_ICON + 5 * 2, ypos + LCD_FONT * j++, LCD_Width, LCD_Height, LCD_FONT, info, WHITE);
    LCD_SHOW_STRING(LCD_ICON + 5 * 2, ypos + LCD_FONT * j++, LCD_Width, LCD_Height, LCD_FONT, COPYRIGHT, WHITE);
    sram_init();
    LCD_SHOW_STRING(5, ypos + LCD_FONT * j++, LCD_Width, LCD_Height, LCD_FONT, "Start Checking Hardware Init", WHITE);
    if (system_exsram_test(5, ypos + LCD_FONT * j++))
        system_error_show(5, ypos + LCD_FONT * j++, "EX Memory Test Error!");
    /* 外部SRAM检测 SRAM检测后,会搞乱内存分配,得重新初始化内存管理*/
    my_mem_init(SRAMEX);
    LCD_SHOW_STRING(5, ypos + LCD_FONT * j, LCD_Width, LCD_Height, LCD_FONT, "RAM Inside(KB):", WHITE);
    LCD_SHOW_XNUM(5 + LCD_OFFSET, ypos + LCD_FONT * j++, MEM1_MAX_SIZE / 1024, 4, LCD_FONT, 0, WHITE);
    LCD_SHOW_STRING(5, ypos + LCD_FONT * j, LCD_Width, LCD_Height, LCD_FONT, "RAM CPU(KB):", WHITE);
    LCD_SHOW_XNUM(5 + LCD_OFFSET, ypos + LCD_FONT * j++, MEM2_MAX_SIZE / 1024, 4, LCD_FONT, 0, WHITE);
    LCD_SHOW_STRING(5, ypos + LCD_FONT * j, LCD_Width, LCD_Height, LCD_FONT, "RAM Extend(KB):", WHITE);
    LCD_SHOW_XNUM(5 + LCD_OFFSET, ypos + LCD_FONT * j++, MEM3_MAX_SIZE / 1024, 4, LCD_FONT, 0, WHITE);
    LCD_BUFF = (uint16_t *)mymalloc(SRAMEX, LCD_Width * LCD_Height);
    if (!LCD_BUFF)
    {
        system_error_show(5, ypos + LCD_FONT * j++, "UI BUF MALLOC Error!");
    }
    INFO("Memory Test OK!");
    res = W25QXX_Init();
    if (res != W25Q16)
    {
        system_error_show(5, ypos + LCD_FONT * j++, "Flash Error!");
    }
    LCD_SHOW_STRING(5, ypos + LCD_FONT * j, LCD_Width, LCD_Height, LCD_FONT, "Flash ID:", WHITE);
    LCD_SHOW_XNUM(5 + LCD_OFFSET, ypos + LCD_FONT * j++, res, 4, LCD_FONT, 0, WHITE);
    INFO("Flash ID: %04X", res);
    /* 检测是否需要擦除SPI FLASH? */
    res = key_scan(1);
    if (res == KEY0_PRES) /* 启动的时候，按下WKUP按键，则擦除SPI FLASH字库和文件系统区域 */
    {
        DBG("A key is detected, and the Flash is about to be erased");
        system_files_erase(5, ypos + LCD_FONT * j);
        j += 3;
    }
    lcd_log_show(5, ypos + LCD_FONT * j++, "FATFS Check...", WHITE);
    mount(EX_FLASH, 1); /* 挂载SPI FLASH */
    temp = 0;
    do
    {
        temp++;
        res = exfuns_get_free(EX_FLASH, &dtsize, &dfsize); /* 得到FLASH剩余容量和总容量 */
        delay_ms(20);
    } while (res && temp < 20); /* 连续检测20次 */
    if (res == 0X0D) /* 文件系统不存在 */
    {
        lcd_log_show(5, ypos + LCD_FONT * j++, "FATFS not detected, Formatting", RED);
        res = exfuns_format(EX_FLASH);
        if (res == 0)
        {
            res = exfuns_get_free(EX_FLASH, &dtsize, &dfsize); /* 得到FLASH剩余容量和总容量 */
        }
    }
    if (res)
    {
        FATAL("Flash FATFS err:%d!!", res);
    }
    LCD_SHOW_STRING(5, ypos + LCD_FONT * j, LCD_Width, LCD_Height, LCD_FONT, "Flash Capacity(KB):", WHITE);
    LCD_SHOW_XNUM(5 + LCD_OFFSET, ypos + LCD_FONT * j++, dtsize, 4, LCD_FONT, 0, WHITE);
    LCD_SHOW_STRING(5, ypos + LCD_FONT * j, LCD_Width, LCD_Height, LCD_FONT, "Flash Available(KB):", WHITE);
    LCD_SHOW_XNUM(5 + LCD_OFFSET, ypos + LCD_FONT * j++, dfsize, 4, LCD_FONT, 0, WHITE);
    INFO("FLASH TEST OK,Available:%ldKB", dfsize);
    /* SD卡检测 */
    res = sd_init();
    if (res == 0)
    {
        mount(SD_CARD, 1); /* 挂载SPI FLASH */
        temp = 0;
        do
        {
            temp++;
            res = exfuns_get_free(SD_CARD, &dtsize, &dfsize); /* 得到SD卡剩余容量和总容量 */
            delay_ms(20);
        } while (res && temp < 5); /* 连续检测5次 */
    }
    if (res == 0)
    {
        LCD_SHOW_STRING(5, ypos + LCD_FONT * j, LCD_Width, LCD_Height, LCD_FONT, "SD Card Capacity(GB):", WHITE);
        LCD_SHOW_XNUM(5 + LCD_OFFSET, ypos + LCD_FONT * j++, dtsize >> 20, 4, LCD_FONT, 0, WHITE);
        LCD_SHOW_STRING(5, ypos + LCD_FONT * j, LCD_Width, LCD_Height, LCD_FONT, "SD Card Available(GB):", WHITE);
        LCD_SHOW_XNUM(5 + LCD_OFFSET, ypos + LCD_FONT * j++, dfsize >> 20, 4, LCD_FONT, 0, WHITE);
        INFO("SD Card TEST OK, Available:%dGB", dfsize >> 20);
    }
    else
    {
        lcd_log_show(5, ypos + LCD_FONT * j++, "The SD card is not detected and more features cannot be used", YELLOW);
    }

    /* 字库检测 */
    lcd_log_show(5, ypos + LCD_FONT * j++, "SYSTEM Parameter Load...", WHITE);
    /* 系统参数加载 */
    USBD_CDC_DEV_INIT();
    /* 蜂鸣器短叫,提示正常启动 */
    lcd_log_show(5, ypos + LCD_FONT * j++, "SYSTEM Starting...", WHITE);
    xTaskCreate((TaskFunction_t)start_task,          /* 任务函数 */
                (const char *)"start_task",          /* 任务名称 */
                (uint16_t)START_STK_SIZE,            /* 任务堆栈大小 */
                (void *)NULL,                        /* 传入给任务函数的参数 */
                (UBaseType_t)START_TASK_PRIO,        /* 任务优先级 */
                (TaskHandle_t *)&StartTask_Handler); /* 任务句柄 */
    myfree(SRAMIN, info);
    vTaskStartScheduler();
    FATAL("can\'t reach here!!");
}

/**
 * @brief       start_task
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 */
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL(); /* 进入临界区 */
    /* 创建任务1 */
    xTaskCreate((TaskFunction_t)task1,
                (const char *)"task1",
                (uint16_t)TASK1_STK_SIZE,
                (void *)NULL,
                (UBaseType_t)TASK1_PRIO,
                (TaskHandle_t *)&Task1Task_Handler);
    /* 创建任务2 */
    xTaskCreate((TaskFunction_t)startUI,
                (const char *)"task2",
                (uint16_t)TASK2_STK_SIZE,
                (void *)LCD_BUFF,
                (UBaseType_t)TASK2_PRIO,
                (TaskHandle_t *)&Task2Task_Handler);
    vTaskDelete(StartTask_Handler);
    taskEXIT_CRITICAL(); /* 退出临界区 */
}

/**
 * @brief       task1
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 */
void task1(void *pvParameters)
{
    uint32_t task1_num = 0;
    DBG("task1: %ld", task1_num); /* 打印数值 */
    while (1)
    {
        DBG("Error Info:%s", "Error"); /* 打印数值 */
        vTaskDelay(1000);              /* 延时1000ticks */
        LED_TOGGLE;                    /* LED翻转 */
    }
}
